package com.heima.juc.unlock;

import com.heima.juc.n4.UnsafeAccessor;
import lombok.extern.slf4j.Slf4j;
import sun.misc.Unsafe;

import java.time.Duration;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

/**
 * @author : leimingchao
 * @date Date : 2022年02月12日 22:51
 */
@Slf4j(topic = "c.Test8")
public class Test8 {

    public static void main(String[] args) {
        Account5 account5 = new MyAtomicInteger(10000);
        Account5.demo(account5);
    }
}

interface Account5 {

    /**
     * 获得余额
     */
    Integer getBalance();

    void withdraw(Integer amount);

    /**
     * 方法内会启动1000个线程，每个线程做 - 10 元的操作
     * 如果初始余额为10000 那么正确的结果应当是 0
     * @param account
     */
    static void demo(Account5 account) {

        List<Thread> ts = new ArrayList<>();
        for (int i = 0; i < 1000; i++) {
            ts.add(new Thread(() -> {
                account.withdraw(10);
            }));

        }

        LocalDateTime start = LocalDateTime.now();

        ts.forEach(Thread::start);

        ts.forEach(t -> {
            try {
                t.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        LocalDateTime end = LocalDateTime.now();

        System.out.println(account.getBalance() + " cost: " + Duration.between(start, end).toMillis() + " ms");
    }

}

class MyAtomicInteger implements Account5 {

    private volatile int value;

    private static final long valueOffset;

    private static final Unsafe UNSAFE;

    static {
        UNSAFE = UnsafeAccessor.getUnsafe();
        try {
            valueOffset = UNSAFE.objectFieldOffset(MyAtomicInteger.class.getDeclaredField("value"));
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

    public int getValue() {
        return value;
    }

    public void decrement(int amount) {

        while (true) {
            int prev = this.value;
            int next = prev - amount;

            if (UNSAFE.compareAndSwapInt(this, valueOffset, prev, next)) {
                break;
            }
        }
    }

    public MyAtomicInteger(int value) {
        this.value = value;
    }

    @Override
    public Integer getBalance() {
        return getValue();
    }

    @Override
    public void withdraw(Integer amount) {
        decrement(amount);
    }
}
